<?php
/**
 * @name  附件model
 *
 */
namespace app\attach\model;
class Attach extends \think\Model {
	protected $tableName = 'attach';
	protected $fields = [
		'attach_id',
		'mime_type',
		'name',
		'type',
		'size',
		'ext',
		'sha1',
		'savename',
		'savepath',
		'thumb_list',
		'height',
		'width',
		'uid',
		'ctime',
		'_pk' => 'attach_id',
	];
	protected $auto = ['ctime' => NOW_TIME];
	/**
	 * @name 本地数据验证(过滤和自动填充数据)
	 * @author martinsun syh@sunyonghong.com
	 * @access protected
	 * @param array $data 验证的数据数组
	 * @param boolean $auto 是否自动填充数据
	 * @return array 验证后的数据
	 */
	protected function validateData($data = [], $auto = false) {
		if (!$data || !is_array($data)) {
			return false;
		}

		$res = [];
		$this->fields = $this->fields ?: $this->getFields();
		foreach ($data as $k => $v) {
			in_array($k, $this->fields) && $res[$k] = $v;
		}
		if ($auto && !empty($this->auto)) {
			return array_merge($res, $this->auto);
		}
		return $res;
	}
	/**
	 * @name 添加附件
	 * @param array $data 添加的数据
	 * @param boolean $case 对应的配置信息
	 */
	public function addAttach($data = [], $type = '', $case = ['max' => 0]) {
		$map['type'] = $type;
		$map['uid'] = $data['uid'];
		$data = self::validateData($data, true);
		$count = self::getAttachCountByUid($data['uid'], $type);
		if ($case['max'] != 0 && $count >= $case['max']) {
			$attach = $this->getOne($map, 'ctime asc');
			$pathType = (stripos($attach['mime_type'], 'image') !== false) ? 'images' : 'files';
			$old_attach = UPLOAD_PATH . $attach['savepath'] . '/' . $pathType . '/' . $attach['savename'];
			//清理旧文件
			@unlink($old_attach);
			$thumb = unserialize($attach['thumb_list']);
			//清理旧文件的缩略图
			array_walk($thumb, create_function('$v,$k', '@unlink($v);'));
			//更新旧记录
			$map['attach_id'] = $attach['attach_id'];
			$data['thumb_list'] = $data['thumb_list'] ?: '';
			if (!$this->where($map)->save($data)) {
				return 0;
			}
			//删除空文件夹
			self::rm_empty_dir();
			//保存附件id
			$id = $attach['attach_id'];
			unset($attach);
		} else {
			//重置缓存副本
			$this->duplicate = [];
			$id = $this->add($data);
			//更新关联数据的条件
			if (isset($case['callback']) && $count + 1 >= (int) $case['max']) {
				self::callBackFun($case['callback'], $data['uid'], $id);
			}
		}
		//清理缓存
		cleanData($uid, $type);
		return encrypt_id($id);
	}
	/**
	 * @name 同步更新操作,用于更新包含有附件ID的关联数据
	 * @access protected
	 * @desc 用于保存附件正常后的执行回调方法
	 * @param array $callback 回调的数据信息 包含
	 *  0 => 回调方法所在的类型 model or action
	 *  1 => 回调执行的类
	 *  2 => 回调执行的方法
	 *  3 => 回调执行的方法所需参数,该参数会以数组形式传入
	 * @return 执行回调的结果
	 */
	protected function callBackFun($callback = [], $uid = 0, $attach_id = 0) {
		if (!$callback || !$uid || !$attach_id) {
			return false;
		}

		list($action, $class, $funtion, $params) = $callback;
		foreach ($params as &$v) {
			if ($v === 'id') {
				$v = $attach_id;
			} elseif ($v === 'uid') {
				$v = $uid;
			}
			continue;
		}
		//手动释放
		unset($v);
		$class = $action === 'action' ? A($class) : model($class);
		return call_user_func([$class, $funtion], $params);
	}
	/**
	 * @name 检测本地数据是否已经存在了记录,存在则返回数据
	 * @param array $map 条件
	 * @return boolean or array 如果存在了数据 将返回数据信息
	 */
	public function hasOne($map) {
		$oneAttach = $this->where($map)->find();
		return $oneAttach ?: false;
	}

	/**
	 * @name 删除所有空目录
	 * @access protected
	 * @param String $path 目录路径
	 */
	protected function rm_empty_dir($path) {
		$path = $path ?: UPLOAD_PATH;
		if (is_dir($path) && ($handle = opendir($path)) !== false) {
			while (($file = readdir($handle)) !== false) {
            // 遍历文件夹
				if ($file != '.' && $file != '..') {
					$curfile = $path . '/' . $file; // 当前目录
					if (is_dir($curfile)) {
                    // 目录
						self::rm_empty_dir($curfile); // 如果是目录则继续遍历
						if (count(scandir($curfile)) == 2) {//目录为空,=2是因为.和..存在
							rmdir($curfile); // 删除空目录
						}
					}
				}
			}
			closedir($handle);
		}
	}

	/**
	 * @name 根据附件ID获取附件地址
	 * @access public
	 * @param int $attach_id 附件ID
	 * @param array 附件的原有信息
	 * @return array $attach 附件信息
	 */
	public function getAttachUrl($attach_id = 0, $data = []) {
		if (!$attach_id && is_array($data)) {
			$attach_id = $data['attach_id'];
		}
		if (!$attach_id) {
			return [];
		}

		if (!$data) {
			$reg = '/^[1-9]\d{0,}$/';
			if (!preg_match($reg, $attach_id, $res)) {
				$attach_id = decrypt_id($attach_id);
			}
			$data = $this->where('attach_id=' . $attach_id)->find();
		}
		$attach = [];
		if ($data) {
			$pathType = ((stripos($data['mime_type'], 'image') !== false) || !empty($data['thumb_list'])) ? 'images' : 'files';
			$list['0'] = '/public/uploads/' . $data['savepath'] . '/' . $pathType . '/' . $data['savename'];
			$data['thumb_list'] = (array) unserialize($data['thumb_list']);
			$list = array_merge($list, $data['thumb_list']);
			foreach ($list as &$v) {
				if (!$v) {
					continue;
				}

				$s = stripos($v, '/public');
				$v = 'http://' . $_SERVER['HTTP_HOST'] . substr($v, $s);
				array_push($attach, $v);
			}
			unset($list);
		}
		return $attach;
	}

	/**
	 * @name 获取单条数据
	 * @access public
	 * @param array $map 条件
	 * @param string $order 排序字段
	 * @return array 单条数据 如果没有记录将返回null
	 */
	public function getOne($map = [], $order = 'ctime desc') {
		if (!$map) {
			return [];
		}

		return $this->where($map)->order($order)->find();
	}

	/**
	 * @name 获取指定用户的附件
	 * @access public
	 * @param int $uid 用户UID
	 * @param string $type 附件类型
	 * @param string $order 排序方式 default : ctime desc
	 * @return array 附件数组信息
	 */
	public function getAttachByUid($uid = 0, $type = '', $order = 'ctime desc') {
		$attach = [];
		if ($uid) {
			$map['uid'] = intval($uid);
			$type && $map['type'] = $type;
			$attach = $this->where($map)->order($order)->select();
			foreach ($attach as &$v) {
				$v['attach_id'] = encrypt_id($v['attach_id']);
				$v['uid'] = encrypt_id($v['uid']);
				$pathType = (stripos($v['mime_type'], 'image') !== false) ? 'images' : 'files';
				$_thumb['0'] = '/public/uploads/' . $v['savepath'] . '/' . $pathType . '/' . $v['savename'];
				$_thumb = array_merge($_thumb, unserialize($v['thumb_list']));
				foreach ($_thumb as &$vo) {
					$s = stripos($vo, '/public');
					$vo = 'http://' . $_SERVER['HTTP_HOST'] . substr($vo, $s);
				}
				$v['thumb_list'] = $_thumb;
				unset($_thumb);
			}
		}
		return $attach;
	}
    /**
     * @name 删除附件
     */
    public function deleteAttachById($attach_id = 0){
        $reg = '/^[1-9]\d{0,}$/';
		if (!preg_match($reg, $attach_id, $res)) {
			$attach_id = decrypt_id($attach_id);
		}
		$attach = $this->where('attach_id=' . $attach_id)->find();
        $pathType = (stripos($attach['mime_type'], 'image') !== false) ? 'images' : 'files';
		$old_attach = UPLOAD_PATH . $attach['savepath'] . '/' . $pathType . '/' . $attach['savename'];
		//清理旧文件
		@unlink($old_attach);
		$thumb = unserialize($attach['thumb_list']);
		//清理旧文件的缩略图
		array_walk($thumb, create_function('$v,$k', '@unlink($v);'));
		//更新旧记录
		$map['attach_id'] = $attach['attach_id'];
		if (!$this->where($map)->delete()) {
			return false;
		}
		//删除空文件夹
		self::rm_empty_dir();
        return true;
    }
	/**
	 * @name 检测指定用户指定类型的附件数量
	 * @access public
	 * @param int $uid 用户UID
	 * @param string $type 附件类型
	 * @return int 统计数量
	 */
	public function getAttachCountByUid($uid = 0, $type = '') {
		if ($uid) {
			$map['uid'] = $uid;
			$type && $map['type'] = $type;
			$count = $this->where($map)->count();
		}
		return (int) $count ?: 0;
	}
}